/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// format::out
#include "platform/format.h"
// Stream
#include "kernel/stream.h"
// Traits
#include "type/typetraits.h"
// NX_LOOP
#include "macro/macro.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

// Define trace stream

namespace private_
{
    template <typename P, typename T, bool Check_ = nx_is_pod(T)>
    struct Trace_
    {
        static void _(const T& data)
        {
            format::out<P>(format::check<T>(), data);
        }
    };

    template <typename P>
    struct Trace_<P, bool, true>
    {
        static void _(bool data)
        {
            format::out<P>(data ? "true" : "false");
        }
    };

    template <typename P, typename T>
    struct Trace_<P, T, false>
    {
        static void _(const T& data)
        {
            format::out<P>(format::check<pvoid>(), data);
        }
    };
}

template <typename P, typename T>
struct Trace
{
    static void _(const T& data)
    {
        private_::Trace_<P, T>::_(data);
    }
};

template <typename P>
struct TraceStream
{
    struct P_stream_
    {
        template <typename T>
        void _(const T& data)
        {
            Trace<P, T>::_(data);
        }
    };
    typedef Stream<P_stream_> ret;
};

template <typename P>
inline const Stream<P>& ent(const Stream<P>& stream)
{
    return stream << "\n";
}

template <size_t S, typename P>
inline const Stream<P>& ent(const Stream<P>& stream)
{
    for(size_t i = 0; i < S; ++i)
        stream << "\n";
    return stream;
}

// nx_out, nx_err, nx_log

typedef TraceStream<nx::format::by_cout>::ret out;
typedef TraceStream<nx::format::by_cerr>::ret err;
typedef TraceStream<nx::format::by_clog>::ret log;

#ifndef nx_out
#define nx_out(...) nx::out(),##__VA_ARGS__
#endif

#ifndef nx_err
#define nx_err(...) nx::err(),##__VA_ARGS__
#endif

#ifndef nx_log
#define nx_log(...) nx::log(),##__VA_ARGS__
#endif

// Define trace

#ifndef nx_trace
#define nx_trace(...) \
    nx::err() << __FILE__ << "\n" << __NX_FUNCTION__ << ", L:" << __LINE__ << " ->: "; \
    nx::err() ,##__VA_ARGS__
#endif

// nx_check macro

#define NX_CHECK_PARAMS_E(args, i, nul) (!(NX_ARGS_F1(args)))
#define NX_CHECK_PARAMS_F(args, i, nxt) NX_CHECK_PARAMS_E(NX_ARGS_(args), i, ) || nxt
#define NX_CHECK_PARAMS_I(args)         NX_ARGS_L1(args)
#define NX_CHECK_PARAMS(...) \
    NX_LOOP(NX_CONTER(__VA_ARGS__), NX_CHECK_PARAMS_F, NX_CHECK_PARAMS_I, NX_CHECK_PARAMS_E, , __VA_ARGS__)

#define NX_CHECK_TRACES_E(args, i, nul) << "\t" << NX_STR(NX_ARGS_F1(args)) << " ->: " << NX_ARGS_F1(args) << "\n"
#define NX_CHECK_TRACES_F(args, i, nxt) NX_CHECK_TRACES_E(NX_ARGS_(args), i, ) nxt
#define NX_CHECK_TRACES_I(args)         NX_ARGS_L1(args)
#define NX_CHECK_TRACES(...) \
    NX_LOOP(NX_CONTER(__VA_ARGS__), NX_CHECK_TRACES_F, NX_CHECK_TRACES_I, NX_CHECK_TRACES_E, , __VA_ARGS__)

#define nx_check(...) \
    if (NX_CHECK_PARAMS(__VA_ARGS__)) \
    { \
        nx_trace() << "Check ERROR!\n\n" \
                   NX_CHECK_TRACES(__VA_ARGS__) \
                   << "\n"; \
    } \
    else

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
